package br.edu.ufcg.embedded.eolica.persistence;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Vector;

import javax.microedition.rms.InvalidRecordIDException;
import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.RecordStoreFullException;
import javax.microedition.rms.RecordStoreNotFoundException;
import javax.microedition.rms.RecordStoreNotOpenException;

import br.edu.ufcg.embedded.eolica.domain.Attribute;
import br.edu.ufcg.embedded.eolica.domain.Station;
import br.edu.ufcg.embedded.eolica.domain.User;
import br.edu.ufcg.embedded.eolica.exception.PersistenceException;
import br.edu.ufcg.embedded.eolica.util.MySerializableResource;


/**
 * @author Thiago Sales <A
 *         HREF="mailto:thiagobruno@embedded.ufcg.edu.br">thiagobruno
 * @embedded.ufcg.edu.br</A>
 */

public class Rms {

	private Class clz;
	private RecordStore rs = null;
	private String recordName;

	public static final String USER_RECORD = "USER_RECORD";
	public static final String STATION_RECORD = "STATION_RECORD";
	public static final String ATTRIBUTE_RECORD = "ATTRIBUTE_RECORD";

	public Rms(String recordName) {
		this.recordName = recordName;

		if (recordName.equals(USER_RECORD)) {
			clz = User.class;
		} else if (recordName.equals(STATION_RECORD)) {
			clz = Station.class;
		} else if (recordName.equals(ATTRIBUTE_RECORD)) {
			clz = Attribute.class;
		}
	}

	private void openRecordStore() throws PersistenceException {
		try {
			rs = RecordStore.openRecordStore(recordName, true);
		} catch (RecordStoreFullException e) {
			throw new PersistenceException(
					"Error while opening record store. Record Store full!");
		} catch (RecordStoreNotFoundException e) {
			throw new PersistenceException(
					"Error while opening record store. Record Store not found!");
		} catch (RecordStoreException e) {
			throw new PersistenceException("Error while opening record store.");
		}

	}

	private void closeRecStore() throws PersistenceException {
		try {
			rs.closeRecordStore();
		} catch (RecordStoreNotOpenException e) {
			throw new PersistenceException(
					"Error while closing record store. Record store not open!");
		} catch (RecordStoreException e) {
			throw new PersistenceException("Error while closing record store.");
		}

	}

	public int save(MySerializableResource resource)
			throws PersistenceException {

		try {
			openRecordStore();
			ByteArrayOutputStream output = new ByteArrayOutputStream();
			resource.write(output);
			byte[] data = output.toByteArray();
			int id = rs.addRecord(data, 0, data.length);
			output.reset();
			output.close();
			return id;
		} catch (IOException e) {
			throw new PersistenceException(
					"Error during writing monitoring data: " + recordName);
		} catch (RecordStoreNotOpenException e) {
			throw new PersistenceException(
					"Error during writing monitoring data: " + recordName
							+ ". RecordStore not open!");
		} catch (RecordStoreFullException e) {
			throw new PersistenceException(
					"Error during writing monitoring data: " + recordName
							+ ". RecordStore full!");
		} catch (RecordStoreException e) {
			throw new PersistenceException(
					"Record store error during writing monitoring data: "
							+ recordName);
		} finally {
			closeRecStore();
		}

	}

	public Vector readAll() throws PersistenceException {

		try {
			openRecordStore();
			Vector resources = new Vector();
			if (rs != null) {
				MySerializableResource resource = null;
				byte[] data = null;
				ByteArrayInputStream byteInput = null;
				int recordId;

				RecordEnumeration enumerate = rs.enumerateRecords(null, null,
						false);
				while (enumerate.hasNextElement()) {
					resource = (MySerializableResource) clz.newInstance();
					recordId = enumerate.nextRecordId();

					data = new byte[rs.getRecordSize(recordId)];
					byteInput = new ByteArrayInputStream(data);

					rs.getRecord(recordId, data, 0);
					resource.read(byteInput);
					resource.setId(recordId);
					resources.addElement(resource);
				}

				return resources;
			} else {
				throw new PersistenceException("Record store " + recordName
						+ " not opened!");
			}
		} catch (RecordStoreNotOpenException e) {
			throw new PersistenceException(
					"Error during reading monitoring data: " + recordName
							+ ". RecordStore not open!");
		} catch (InvalidRecordIDException e) {
			throw new PersistenceException(
					"Error during reading monitoring data: " + recordName
							+ ". Invalid record id!");
		} catch (RecordStoreException e) {
			throw new PersistenceException(
					"Record store error during reading monitoring data: "
							+ recordName);
		} catch (IOException e) {
			throw new PersistenceException(
					"Record store error during reading monitoring data: "
							+ recordName);
		} catch (InstantiationException e) {
			throw new PersistenceException(
					"Record store error during reading monitoring data: "
							+ recordName);
		} catch (IllegalAccessException e) {
			throw new PersistenceException(
					"Record store error during reading monitoring data: "
							+ recordName);
		} finally {
			closeRecStore();
		}
	}

	public boolean update(MySerializableResource targetResource)
			throws PersistenceException {
		try {
			openRecordStore();
			if (rs != null) {
				MySerializableResource resource = null;
				RecordEnumeration enumerate = rs.enumerateRecords(null, null,
						false);

				resource = (MySerializableResource) clz.newInstance();

				int recordId;
				boolean found = false;

				while (enumerate.hasNextElement()) {
					recordId = enumerate.nextRecordId();
					resource.setId(recordId);

					if (resource.equals(targetResource)) {
						ByteArrayOutputStream outputByte = new ByteArrayOutputStream();
						targetResource.write(outputByte);
						byte[] newResource = outputByte.toByteArray();
						rs.setRecord(recordId, newResource, 0,
								newResource.length);
						outputByte.reset();
						outputByte.close();
						found = true;
					}
				}

				return found;
			} else {
				throw new PersistenceException("Record store " + recordName
						+ " not opened!");
			}
		} catch (RecordStoreNotOpenException e) {
			throw new PersistenceException(
					"Error during reading monitoring data: " + recordName
							+ ". RecordStore not open!");
		} catch (InvalidRecordIDException e) {
			throw new PersistenceException(
					"Error during reading monitoring data: " + recordName
							+ ". Invalid record id!");
		} catch (RecordStoreException e) {
			throw new PersistenceException(
					"Record store error during reading monitoring data: "
							+ recordName);
		} catch (IOException e) {
			throw new PersistenceException(
					"Record store error during reading monitoring data: "
							+ recordName);
		} catch (InstantiationException e) {
			throw new PersistenceException(
					"Record store error during reading monitoring data: "
							+ recordName);
		} catch (IllegalAccessException e) {
			throw new PersistenceException(
					"Record store error during reading monitoring data: "
							+ recordName);
		} finally {
			closeRecStore();
		}

	}

	public void deleteAll() throws PersistenceException {
		try {
			String[] records = RecordStore.listRecordStores();
			for (int i = 0; i < records.length; i++) {
				if(records[i].equals(recordName))
					RecordStore.deleteRecordStore(recordName);
			}
			
		} catch (RecordStoreNotFoundException e) {
			throw new PersistenceException(
					"Error during removing record store: " + recordName
							+ ". Record store not found!");
		} catch (RecordStoreException e) {
			throw new PersistenceException(
					"Record store error during removing record store: "
							+ recordName);
		}
	}

	public boolean delete(MySerializableResource targetResource)
			throws PersistenceException {
		try {
			openRecordStore();
			if (rs != null) {
				MySerializableResource resource = null;
				RecordEnumeration enumerate = rs.enumerateRecords(null, null,
						false);

				resource = (MySerializableResource) clz.newInstance();

				int recordId;
				boolean deleted = false;

				while (enumerate.hasNextElement()) {
					recordId = enumerate.nextRecordId();
					resource.setId(recordId);

					if (resource.equals(targetResource)) {
						rs.deleteRecord(recordId);
						deleted = true;
					}
				}

				return deleted;
			} else {
				throw new PersistenceException("Record store " + recordName
						+ " not opened!");
			}
		} catch (RecordStoreNotOpenException e) {
			throw new PersistenceException(
					"Error during reading monitoring data: " + recordName
							+ ". RecordStore not open!");
		} catch (InvalidRecordIDException e) {
			throw new PersistenceException(
					"Error during reading monitoring data: " + recordName
							+ ". Invalid record id!");
		} catch (RecordStoreException e) {
			throw new PersistenceException(
					"Record store error during reading monitoring data: "
							+ recordName);
		} catch (IOException e) {
			throw new PersistenceException(
					"Record store error during reading monitoring data: "
							+ recordName);
		} catch (InstantiationException e) {
			throw new PersistenceException(
					"Record store error during reading monitoring data: "
							+ recordName);
		} catch (IllegalAccessException e) {
			throw new PersistenceException(
					"Record store error during reading monitoring data: "
							+ recordName);
		} finally {
			closeRecStore();
		}
	}
}
